ASN.1 Компілятор ASN1SCG
У статті представлений мінімальний Swift "ASN.1 компілятор"
на мові Erlang.
Автор — Максим Сохацький, 2023-8-8.
Зміст
1) Контест ASN.1 компіляторів
2) X.509 DER vs Erlang BERT/ETF
3) Бібліотека Apple
4) Erlang AST ASN.1 Swift кодогенаратора
Вступ
Питання вибору серіалізатора залежить від типу інфраструктури в компанії. Якшо ми говоримо про гомогенні інтерфейси та бібліотеки прикладного програмування то зазвичай вони достатньо розвинені аби автоматично генерувати структури для основних мов програмування для яких анонсується серіалізатор. Хоча формально йдеться про AST трансформацію з мови визначення типів структур в цільову мову програмування, такі системи називаються компіляторами мови визначення даних.
У світі існує багато рантаймів, і кожен з них пропонує свій рідний серіалізатор, який в незмінному вигляді представляє дані які циркулюють в рантаймі або віртуальній машині. Так на мовах .NET, Java, Haskell, Erlang є такі природні серіалазотори. В гомогенних архітектурах, де все побудовано навколо однії мови програмування, прийнято використовувати цей серіалізатор як основний для всіх сервісів написаних навіть на різних мовах програмування, так як BERT-RPC протокол який використовувався в Github.
Окремо виділяються формати які не прив'язані до однієї мови, а пропонуються як універсальні серіалізатори, такі як IDL COM/DCOM, ASN.1 DER, Erlang BERT/ETF, GRPC Gproto, SOAP XML/WBXML, та інші бінарні мови і серіалізатори.
Всі сертифікати в браузері, SSH ключі, PGP/GPG ключі, закриті і відкриті конверти, криптографічні повідомлення CMS, протоколи видачі сертифікатів, LDAP директорія та ще багато іншого включачи GSM/LTE та майже всі телекомунікаційні повідомлення визначаються за допомогою ASN.1.
Компілятори
Я хотів зробити огляд ASN.1 компіляторів, але з представлених безкоштовних жоден крім Erlang-ового не компілює повний набір ASN.1 файлів проекта SYNRC CA. Ні C-шний кацапський який використовується в Apple ASN1C, Ні F#-повий ASN1SCC. Залишається сподіватися, що генератор С-шного коду Фабріса Белара ASN1CC зможе це зробити однак з огляду на його клієнтів навряд чи ми про це дізнаємося, так шоб прямо розказати в блозі.
Я подивився на помилки представлених на сайті ITU компіляторів і складається враження що сумісність з самим ж файлами дефініціями ITU ніхто не перевіряє навіть мануально. Просто вивішують в залікову таблицю будь-кого хто успішно розпарсав сабсет ASN.1 і згенерував якийсь граничні імплементація для свого випадку.
Тестовий набір файлів
Ось текстовий сет з сайту ITU яким я перевіряв компілятори.
AESKeyWrapWithPad-02.asn1
AESKeyWrapWithPad-88.asn1
ANSI-X9-42.asn1
ANSI-X9-62.asn1
AlgorithmInformation-2009.asn1
AttributeCertificateVersion1-2009.asn1
AuthenticationFramework.asn1
BasicAccessControl.asn1
CHAT.asn1
CMS-AES-CCM-and-AES-GCM-2009.asn1
CMSAesRsaesOaep-2009.asn1
CMSECCAlgs-2009-02.asn1
CMSECDHAlgs-2017.asn1
CertificateExtensions.asn1
Character-Coding-Attributes.asn1
Character-Presentation-Attributes.asn1
Character-Profile-Attributes.asn1
Colour-Attributes.asn1
CryptographicMessageSyntax-2009.asn1
CryptographicMessageSyntax-2010.asn1
CryptographicMessageSyntaxAlgorithms-2009.asn1
DOR-definition.asn1
Default-Value-Lists.asn1
DirectoryAbstractService.asn1
Document-Profile-Descriptor.asn1
EnrollmentMessageSyntax-2009.asn1
ExtendedSecurityServices-2009.asn1
External-References.asn1
Geo-Gr-Coding-Attributes.asn1
Geo-Gr-Presentation-Attributes.asn1
Geo-Gr-Profile-Attributes.asn1
ISO-STANDARD-9541-FONT-ATTRIBUTE-SET.asn1
ISO9541-SN.asn1
Identifiers-and-Expressions.asn1
InformationFramework.asn1
KEP.asn1
LDAP.asn1
Layout-Descriptors.asn1
Link-Descriptors.asn1
Location-Expressions.asn1
Logical-Descriptors.asn1
MultipleSignatures-2010.asn1
OCSP.asn1
PKCS-10.asn1
PKCS-12.asn1
PKCS-5.asn1
PKCS-7.asn1
PKCS-8.asn1
PKCS-9.asn1
PKIX-CommonTypes-2009.asn1
PKIX-X400Address-2009.asn1
PKIX1-PSS-OAEP-Algorithms-2009.asn1
PKIX1Explicit-2009.asn1
PKIX1Explicit88.asn1
PKIX1Implicit-2009.asn1
PKIX1Implicit88.asn1
PKIXAlgs-2009.asn1
PKIXAttributeCertificate-2009.asn1
PKIXCMP-2009.asn1
PKIXCRMF-2009.asn1
Raster-Gr-Coding-Attributes.asn1
Raster-Gr-Presentation-Attributes.asn1
Raster-Gr-Profile-Attributes.asn1
SMIMESymmetricKeyDistribution-2009.asn1
SecureMimeMessageV3dot1-2009.asn1
SelectedAttributeTypes.asn1
Style-Descriptors.asn1
Subprofiles.asn1
Temporal-Relationships.asn1
Text-Units.asn1
UpperBounds.asn1
UsefulDefinitions.asn1
Videotex-Coding-Attributes.asn1
Фірмові і стандартні
Тут розглядаються фірмова бібліотека RPC та стандартна ASN1X компанії SYNRC для бінарної серіалізації внутрішнього бінарного представлення віртуальної машини Erlang та бінарного представлення сімейства розміточних форматів і їх парсерів BER, DER, PER, кодери і декодери яких генеруються з мови специфікацій та протоколів ASN.1.
Ericsson Erlang BERT/ETF
Серед фірмових можна відзначити Ericsson Binary Erlang Term Format, який використовується в промисловості більше 10 років починаючи з Github. SYNRC використовує бібліотеку SYNRC RPC яка генерує кодери і декодери (API SDK) для будь яких Erlang структур на наступні мови програмування та мови визначення протоколів:
1) IDL/COM;
2) JavaScript;
3) Google gproto;
4) Apple Swift;
5) Erlang validation.
Завдяки генератору BERT парсерів SYNRC RPC компанія NYNJA змогла уніфіковано працювати з об'єктами системи в двох форматах BERT і GPROTO і залишилася після апробації на BERT.
ITU IETF ASN.1 BER/DER/PER Apple/Microsoft/OpenSSL
До стандартних можна віднести бінарні формати для серіалізації сертифікатів та обгорток криптографічних ключів які використовуються в PKI X.509. Завдяки промисловому і повному компілятору ASN.1 у складі Erlang/OTP ми маємо змогу побудувати повністю API SDK інфраструктуру на Erlang мінімальними зусиллями не гублячи сумісність з фірмовим форматом BERT/ETF, так як він все одно використовується в середині віртуальної машини після конвертації. Так була продемонстрована сумісність протоколів CHAT BERT (HRL) та результату ASN.1 компіляції CHAT BER.
1) ASN1C;
2) ASN1CC;
3) ASN1SCC;
4) ASN1SCG (Swift Code Generation);
5) ASN1JCG (Java Code Generation).
В даній статті розказується про один з двох генераторів коду DER/BER/PER кодерів і декодерів SYNRC для мови Swift — ASN1SCG, який генерує код невідмінний від схеми кодування і декодування яку Apple пропонує в свої бібліотеках asn1, crypto та certificates.
Google gproto/GRPC
В першу чергу цей формат набув популярності на пристроях Apple, так як протокол основного AP сховища Apple Cassandra використовує цей формат. Значною проблему є переускладеність кодогенератора і інфраструктури компілятора.
Бібліотеки Apple
Починаючи з вести 2020 року, коли Apple анонсувала CryptoKit для X.509 інфраструктури зокрема, компанія випустила наступні бібліотеки, які показується стандартний механізм який Apple передбачає для Swift авторів як вони мусять користуватися PKI X.509 обгортками, сертифікатами, ключами та взагалі будь-якими ASN.1 протоколами.
1) apple/swift-asn1
2) apple/swift-crypto
3) apple/swift-certificates
SYNRC ASN1SCG — це ASN.1 компілятор в мову Swift з використанням схеми кодування Apple, написаний на мові Erlang компанією SYNRC. Тут показуються приклади генерації кодерів і декодерів для деяких структур сімейства PKI X.509 в форматі прикладів Apple (насправді відрізнити конкретно ці неможливо).
ECDSASignature
ASN.1 визначення структури ECDSASignature.
ECDSASignature ::= SEQUENCE {
r INTEGER,
s INTEGER }
Кодер
func serialize(into coder: inout DER.Serializer,
withIdentifier identifier: ASN1Identifier) throws {
try coder.appendConstructedNode(identifier: identifier) { coder in
try coder.serialize(self.r)
try coder.serialize(self.s)
}
}
Декодер
init(derEncoded root: ASN1Node,
withIdentifier identifier: ASN1Identifier) throws {
self = try DER.sequence(rootNode, identifier: identifier) { nodes in
let r = try ArraySlice(derEncoded: &nodes)
let s = try ArraySlice(derEncoded: &nodes)
return ECDSASignature(r: r, s: s)
}
}
SubjectPublicKeyInfo
ASN.1 визначення структури SubjectPublicKeyInfo.
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING }
Кодер
func serialize(into coder: inout DER.Serializer,
withIdentifier identifier: ASN1Identifier) throws {
try coder.appendConstructedNode(identifier: identifier) { coder in
try coder.serialize(self.algorithmIdentifier)
try coder.serialize(self.key) } }
Декодер
init(derEncoded root: ASN1Node,
withIdentifier identifier: ASN1Identifier) throws {
self = try DER.sequence(root, identifier: identifier) { nodes in
let algid = try AlgorithmIdentifier(derEncoded: &nodes)
let key = try ASN1BitString(derEncoded: &nodes)
return SubjectPublicKeyInfo(algorithmIdentifier: algid, key: key) } }
TBSCertificate
ASN.1 визначення структури TBSCertificate.
TBSCertificate ::= SEQUENCE {
version [0] Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name,
validity Validity,
subject Name,
subjectPublicKeyInfo SubjectPublicKeyInfo,
issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
extensions [3] Extensions OPTIONAL }
Version ::= INTEGER { v1(0), v2(1), v3(2) }
CertificateSerialNumber ::= INTEGER
UniqueIdentifier ::= BIT STRING
Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
Кодер
init(derEncoded root: ASN1Node,
withIdentifier identifier: ASN1Identifier) throws {
self = try DER.sequence(rootNode, identifier: identifier) { nodes in
let version = try DER.decodeDefaultExplicitlyTagged(&nodes,
tagNumber: 0, tagClass: .contextSpecific, defaultValue: Int(0))
guard (0...2).contains(version) else {
throw ASN1Error.invalidASN1Object(
reason: "Invalid X.509 version \(version)")
}
let serialNumber = try ArraySlice(derEncoded: &nodes)
let signature = try AlgorithmIdentifier(derEncoded: &nodes)
let issuer = try DistinguishedName(derEncoded: &nodes)
let validity = try Validity(derEncoded: &nodes)
let subject = try DistinguishedName(derEncoded: &nodes)
let subjectPublicKeyInfo = try SubjectPublicKeyInfo(derEncoded: &nodes)
let issuerUniqueID = try DER.optionalExplicitlyTagged(&nodes,
tagNumber: 1, tagClass: .contextSpecific) {
try UniqueIdentifier(derEncoded: $0)
}
let subjectUniqueID = try DER.optionalExplicitlyTagged(&nodes,
tagNumber: 2, tagClass: .contextSpecific) {
try UniqueIdentifier(derEncoded: $0)
}
let extensions = try DER.optionalExplicitlyTagged(&nodes,
tagNumber: 3, tagClass: .contextSpecific) {
try DER.sequence(of: Certificate.Extension.self,
identifier: .sequence, rootNode: $0)
}
return TBSCertificate(
version: Certificate.Version(rawValue: version),
serialNumber: Certificate.SerialNumber(
bytes: serialNumber),
signature: Certificate.SignatureAlgorithm(
algorithmIdentifier: signature),
issuer: issuer,
validity: validity,
subject: subject,
publicKey: try Certificate.PublicKey(
spki: subjectPublicKeyInfo),
issuerUniqueID: issuerUniqueID,
subjectUniqueID: subjectUniqueID,
extensions: try Certificate.Extensions(extensions ?? [])
)
}
}
Декодер
func serialize(into coder: inout DER.Serializer,
withIdentifier identifier: ASN1Identifier) throws {
try coder.appendConstructedNode(identifier: identifier) { coder in
if self.version != .v1 {
try coder.serialize(self.version.rawValue,
explicitlyTaggedWithTagNumber: 0, tagClass: .contextSpecific)
}
try coder.serialize(self.serialNumber.bytes)
try coder.serialize(AlgorithmIdentifier(self.signature))
try coder.serialize(self.issuer)
try coder.serialize(self.validity)
try coder.serialize(self.subject)
try coder.serialize(SubjectPublicKeyInfo(self.publicKey))
if let issuerUniqueID = self.issuerUniqueID {
try coder.serialize(issuerUniqueID,
explicitlyTaggedWithTagNumber: 1, tagClass: .contextSpecific)
}
if let subjectUniqueID = self.subjectUniqueID {
try coder.serialize(subjectUniqueID,
explicitlyTaggedWithTagNumber: 2, tagClass: .contextSpecific)
}
if self.extensions.count > 0 {
try coder.serialize(explicitlyTaggedWithTagNumber: 3,
tagClass: .contextSpecific) { coder in
try coder.serializeSequenceOf(extensions) }
}
}
}
Кодогенерація на Erlang в Swift
[x] Sequence
[x] Choice
[x] Enumeration
[x] Integer enumeration
[x] Sequence Of
[x] Any
[x] Null
[x] Integer
[x] Strings
Висновки
Я взяв наймолодші вендор бібліотеки Apple для схем кодування і декодування ASN.1 форматів BER/DER і існуючу структуру ASN.1 компілятора Ericsson, єдиного вільного повного, і написав генератор перших (схем кодування) використовуючи типову інформацію другого (ASN.1 компілятора Erlang/OTP).
Щодо ефективності, то всі зауваження щодо неї можна направляти напряму Apple, так як наш компілятор намагається просто бути максимально сумісним і робити вигляд шо цей код був написаний співробітниками Apple.
Якщо ж досліджувати моделі бездоганної або гранично-ефективної компіляції, то вона може бути досягнена виключно генерацією ассемблерних інструкцій і по можливості векторних і паралельних схем парсінгу з представленнями доведеннями ефективності через аналіз заповнення конвеєрів процесора і підрахунку циклів процесора на операції.
[1]. ITU-T ASN.1 Compilers
[2]. Apple Swift ASN.1 Library Documentation.
[3]. Let's Encrypt. Ласкаво просимо в ASN.1 і DER
[4]. Swift Crypto Library Documentation
[5]. Swift Certificates Library Documentation
[6]. Olivier Dubuisson. ASN.1 Communication between Heterogeneous Systems